home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
3D Images
/
3D Images.iso
/
programs
/
amiga
/
rayshade
/
libshade
/
shade.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-12
|
13KB
|
425 lines
/*
* shade.c
*
* Copyright (C) 1989, 1991, Craig E. Kolb
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
* shade.c,v 4.1 1994/08/09 08:04:57 explorer Exp
*
* shade.c,v
* Revision 4.1 1994/08/09 08:04:57 explorer
* Bump version to 4.1
*
* Revision 1.1.1.1 1994/08/08 04:52:17 explorer
* Initial import. This is a prerelease of 4.0.6enh3, or 4.1 possibly.
*
* Revision 4.0 91/07/17 14:47:36 kolb
* Initial version.
*
*/
#include "rayshade.h"
#include "libtext/texture.h"
#include "libsurf/surface.h"
#include "liblight/light.h"
#include "libsurf/atmosphere.h"
#include "options.h"
#include "stats.h"
#include "viewing.h"
Medium TopMedium;
Atmosphere *AtmosEffects;
static void shade(), LightRay(), Lighting(), ReflectRay();
static int TransmitRay();
/*
* Calculate color of ray.
*/
void
ShadeRay(hitlist, ray, dist, back, color, contrib)
HitList *hitlist; /* Information about point of intersection. */
Ray *ray; /* Direction and origin of ray. */
Float dist; /* Distance from origin of intersection. */
Color *back, /* "Background" color */
*color, /* Color to assign current ray. */
*contrib; /* Contribution of this ray to final color */
{
Vector norm, gnorm, pos; /* surface normal, point of intersection */
Surface surf, *stmp; /* surface properties */
int enter, smooth; /* entering ?, gnorm != snorm ?*/
if (hitlist->nodes == 0) {
/*
* No valid intersection. Set distance for atmospheric
* effects and set color of ray to background.
*/
*color = *back;
VecAddScaled(ray->pos, FAR_AWAY, ray->dir, &pos);
if (!ray->media && AtmosEffects)
Atmospherics(AtmosEffects, ray, FAR_AWAY, &pos, color);
return;
}
/*
* Compute normal, surface properties, etc.
*/
stmp = GetShadingSurf(hitlist);
surf = *stmp;
enter = ComputeSurfProps(hitlist, ray, &pos, &norm, &gnorm, &surf,
&smooth);
Stats.HitRays++;
/*
* Calculate ray color.
*/
shade(&pos, ray, &norm, &gnorm, smooth, enter, &surf, back, color,
contrib);
if (!ray->media && AtmosEffects)
Atmospherics(AtmosEffects, ray, dist, &pos, color);
}
/*
* Perform lighting calculations based on surface normal & other properties,
* incident ray direction and position, and light source properties.
* Spawn any necessary reflected and transmitted rays.
*/
static void
shade(pos, ray, nrm, gnrm, smooth, enter, surf, back, color, contrib)
Vector *pos, *nrm, *gnrm; /* hit pos, shade normal, geo normal */
int smooth; /* true if shading norm and geo norm differ */
int enter; /* TRUE if entering surface */
Ray *ray; /* indicent ray */
Surface *surf; /* properties of hit surface */
Color *back, *color; /* background color, computed color */
Color *contrib; /* contribution to final pixel value */
{
Float k; /* -ray . normal */
Color newcontrib;
Vector refl; /* reflected direction */
Color reflectivity, /* effective surface reflectivity */
intens; /* reflected/transmitted intensity */
Light *lp; /* current light source */
extern Light *Lights; /* list of defined sources */
/*
* Ambient color is always included.
*/
ColorMultiply(surf->amb, Options.ambient, color);
/*
* Calculate direction of reflected ray.
*/
k = -dotp(&ray->dir, nrm);
VecAddScaled(ray->dir, 2.*k, *nrm, &refl);
/*
* Calculate intensity contributed by each light source.
*/
for (lp = Lights; lp; lp = lp->next)
LightRay(lp, pos, nrm, gnrm, smooth, &refl, surf, ray, color);
if (ray->depth >= Options.maxdepth)
/*
* Don't spawn any transmitted/reflected rays.
*/
return;
/*
* Specular transmission (refraction).
*/
ColorScale(surf->reflect, surf->spec, &reflectivity);
if (surf->transp > EPSILON) {
ColorScale(surf->transp, surf->body, &intens);
ColorMultiply(intens, *contrib, &newcontrib);
if (newcontrib.r > Options.cutoff.r ||
newcontrib.g > Options.cutoff.g ||
newcontrib.b > Options.cutoff.b)
/*
* Transmit ray. If TIR occurs, add transmitted
* component to reflected component. Kinda strange, but...
*/
if (TransmitRay(ray, pos, nrm, k, surf->index,
surf->statten, enter, back, &newcontrib, &intens, color))
ColorAdd(reflectivity, intens, &reflectivity);
}
if (reflectivity.r > EPSILON ||
reflectivity.g > EPSILON ||
reflectivity.b > EPSILON) {
ColorMultiply(reflectivity, *contrib, &newcontrib);
if (newcontrib.r > Options.cutoff.r ||
newcontrib.g > Options.cutoff.g ||
newcontrib.b > Options.cutoff.b)
ReflectRay(ray, pos, &refl, back, &reflectivity,
&newcontrib, color);
}
}
/*
* Lighting calculations
*/
static void
LightRay(lp, pos, norm, gnorm, smooth, reflect, surf, oldray, color)
Light *lp; /* Light source */
Vector *pos, *norm, *gnorm; /* hit pos, shade norm, geo norm */
int smooth; /* true if shade and geo norm differ */
Vector *reflect; /* reflection direction */
Surface *surf; /* surface characteristics */
Ray *oldray; /* indicent ray */
Color *color; /* resulting color */
{
Color lcolor;
Ray newray;
Float costheta, cosalpha, cosphi, dist;
newray.type = SHADOW_RAY;
newray.pos = *pos;
newray.depth = oldray->depth;
newray.sample = oldray->sample;
newray.time = oldray->time;
newray.media = (Medium *)NULL;
LightDirection(lp, pos, &newray.dir, &dist);
costheta = dotp(&newray.dir, norm);
/* ray viewpoint is the position at the light source */
VecAddScaled(newray.pos, dist, newray.dir, &newray.viewpoint);
/* shadow ray "sees" same area at intersection position as parent ray */
cosphi = -dotp(&oldray->dir, norm);
if (cosphi < EPSILON) cosphi = EPSILON;
newray.width = PixelSize(oldray, VecDist(&newray.pos, &oldray->pos)) / cosphi * costheta;
/* shadow rays become smaller as they approach the light source */
newray.viewdist = -dist;
newray.stretch = 1.;
if (smooth) {
cosalpha = dotp(&newray.dir, gnorm);
/*
* If shading normal indicates self-shadowing
* and geom normal indicates no self-shadowing,
* trust the geom normal.
*/
if (costheta <= 0. && cosalpha > 0.)
costheta = cosalpha;
/*
* If geom normal indicates self-shadowing and
* geom normal doesn't, then have to do something
* clever ala Snyder & Barr.
*/
}
if (costheta <= 0.) {
/*
* Light source is on opposite side of surface,
* hence light must be transmitted through...
*/
if (surf->translucency < EPSILON)
return;
if (!LightIntens(lp, &newray, dist,
(int)surf->noshadow, &lcolor))
return;
cosalpha = dotp(&oldray->dir, &newray.dir);
ColorScale(surf->translucency, lcolor, &lcolor);
Lighting(-costheta, cosalpha, &lcolor, &surf->translu,
&surf->body, surf->stexp, color);
} else {
if (!LightIntens(lp, &newray, dist,
(int)surf->noshadow, &lcolor))
return; /* prim is in shadow w.r.t light source */
cosalpha = dotp(reflect, &newray.dir);
Lighting(costheta, cosalpha, &lcolor, &surf->diff,
&surf->spec, surf->srexp, color);
}
}
/*
* Compute shading function (diffuse reflection and specular highlight)
*
* This function *adds* the computed color to "color".
*/
static void
Lighting(costheta, cosalpha, lcolor, diff, spec, coef, color)
Float costheta, cosalpha, coef;
Color *diff, *spec, *color, *lcolor;
{
Float intens;
/*
* Diffuse reflection.
* Falls off as the cosine of the angle between
* the normal and the ray to the light (costheta).
*/
color->r += diff->r * costheta * lcolor->r;
color->g += diff->g * costheta * lcolor->g;
color->b += diff->b * costheta * lcolor->b;
/*
* Specularly reflected highlights.
* Fall off as the cosine of the angle
* between the reflected ray and the ray to the light source.
*/
if (coef < EPSILON || cosalpha <= 0.)
return;
/*
* Specular highlight = cosine of the angle raised to the
* appropriate power.
*/
intens = pow(cosalpha, coef);
color->r += spec->r * intens * lcolor->r;
color->g += spec->g * intens * lcolor->g;
color->b += spec->b * intens * lcolor->b;
}
/*
* Spawn a transmitted ray. Returns TRUE if total internal reflection
* occurs, FALSE otherwise.
*/
static int
TransmitRay(ray, pos, norm, k, index, statten, enter, back, contrib, intens, color)
Ray *ray;
Vector *pos, *norm;
Float k, index, statten;
int enter;
Color *back, *contrib, *intens, *color;
{
int total_int_refl = FALSE;
Ray NewRay;
Float dist, cosi, cosr;
Color newcol;
HitList hittmp; /* Geom intersection record */
NewRay.type = REFRACT_RAY;
NewRay.pos = *pos; /* Origin == hit point */
NewRay.media = ray->media; /* Media == old media */
NewRay.sample = ray->sample;
NewRay.time = ray->time;
NewRay.depth = ray->depth + 1;
if (enter) {
/*
* Entering surface.
*/
if (Refract(&NewRay.dir,
NewRay.media ? NewRay.media->index :
TopMedium.index, index, &ray->dir, norm, k)) {
total_int_refl = TRUE;
} else {
/*
* Push information for new medium.
*/
NewRay.media = MediumPush(index, statten, NewRay.media);
}
} else {
/*
* Exiting surface
* Pop medium from stack.
*/
if (NewRay.media != (Medium *)0)
NewRay.media = NewRay.media->next;
if (Refract(&NewRay.dir, index,
NewRay.media ? NewRay.media->index :
TopMedium.index, &ray->dir, norm, k)) {
total_int_refl = TRUE;
}
}
/*
* At this point, NewRay.media is the medium into which
* the new ray is entering.
*/
if (!total_int_refl) {
/* good for planar refraction only */
/* cosi = cos angle(incident ray, surface normal)
* cosr = cos angle(refracted ray, surface normal) */
cosi = -dotp(&ray->dir, norm);
if (cosi < EPSILON) cosi = EPSILON;
cosr = -dotp(&NewRay.dir, norm);
/*
* nr cosr 2
* NewRay.viewdist = y = -- ( ---- ) * x
* ni cosi
* where x = distance newray origin to oldray viewpoint.
*/
NewRay.viewdist = (NewRay.media ? NewRay.media->index : TopMedium.index) / (ray->media ? ray->media->index : TopMedium.index) *
(cosr * cosr) / (cosi * cosi) *
(ray->viewdist < 0. ? -1. : 1.) * VecDist(&ray->viewpoint, &NewRay.pos);
/* NewRay.viewpoint = NewRay.pos - y * NewRay.dir */
VecAddScaled(NewRay.pos, -NewRay.viewdist, NewRay.dir, &NewRay.viewpoint);
/* both rays "see" same area at intersection point */
NewRay.width = PixelSize(ray, VecDist(&ray->pos, &NewRay.pos)) * cosr / cosi;
NewRay.stretch = 1.;
Stats.RefractRays++;
hittmp.nodes = 0;
dist = FAR_AWAY;
TraceRay(&NewRay, &hittmp, EPSILON, &dist);
ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
ColorMultiply(newcol, *intens, &newcol);
/*
* Attenuate transmitted color. Note that
* if the transmitted ray hit nothing, we still
* perform this computation, as it's possible
* that 'air' has a non-unit statten.
*/
statten = NewRay.media ? NewRay.media->statten :
TopMedium.statten;
if (statten != 1.0) {
statten = pow(statten, dist);
ColorScale(statten, newcol, &newcol);
}
ColorAdd(*color, newcol, color);
/* Free pushed medium */
if (enter)
free((voidstar)NewRay.media);
}
return total_int_refl;
}
static void
ReflectRay(ray, pos, dir, back, intens, contrib, color)
Ray *ray;
Vector *pos, *dir;
Color *back, *intens, *contrib, *color;
{
Ray NewRay;
HitList hittmp; /* Geom intersection record */
Color newcol;
Float dist;
NewRay.type = REFLECT_RAY;
NewRay.pos = *pos; /* Origin == hit point */
NewRay.dir = *dir; /* Direction == reflection */
NewRay.media = ray->media; /* Medium == old medium */
NewRay.sample = ray->sample;
NewRay.time = ray->time;
NewRay.depth = ray->depth + 1;
/* good for planar reflections only */
/* new viewpoint is mirrored old one. width at intersection point is the same */
NewRay.viewdist = (ray->viewdist < 0. ? -1. : 1.) * VecDist(&ray->viewpoint, &NewRay.pos);
NewRay.width = PixelSize(ray, VecDist(&ray->pos, &NewRay.pos));
VecAddScaled(NewRay.pos, -NewRay.viewdist, NewRay.dir, &NewRay.viewpoint);
NewRay.stretch = 1.;
Stats.ReflectRays++;
hittmp.nodes = 0;
dist = FAR_AWAY;
(void)TraceRay(&NewRay, &hittmp, EPSILON, &dist);
ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
ColorMultiply(newcol, *intens, &newcol);
ColorAdd(*color, newcol, color);
}